/*
 * idt.c
 *
 *  Created on: 01/09/2010
 *      Author: Wendell
 *
 *	=== A IDT ===
 *  	A Tabela de Descrição de Interrupções (Interrupt Descriptor Table),
 *  ou IDT, é usada para mostrar ao processador qual Rotina para Serviço de
 *  Interrupção (Interrupt Service Routine (ISR)) chamar para tratar qualquer
 *  uma exceção ou um opcode 'int' (em assembly).
 *  	Entradas IDT são também chamados por Interrupções de Requisições (
 *  Interrupt Requests) sempre que um dispositivo completou uma requisição e
 *  precisa ser servido.
 *
 *  	Cada entrada IDT é similar a uma entrada GDT. Ambos possuem um endereço
 *  base, ambos tem um flag de acesso, e ambos são de 64-bits.
 *  	A maior diferença entre estes dois tipos de descritores estão no
 *  significados destes campos. Em uma IDT, o endereço base especificao no
 *  descritor é realmente o endereço da Rotina para Serviço de Interrupção que
 *  o processador vai chamar quando está interrupção é 'levantada' (chamada).
 *  	Uma entrada IDT não possui limite, ao invés disto este possui um
 *  segmento que precisa ser especificado. O segmento precisa ser o mesmo
 *  segmento que o ISR dado está localizado. Este permite ao processador dar
 *  controle ao kernel através de uma interrupção que ocorreu quando o
 *  processador estava em um ring diferente (principalmente quando uma aplicação
 *  está rodando).
 *
 *  	A flag de acesso de uma entrada IDT são também similares para uma
 *  entrada GDT. Há um campo para dizer se o descritor está realmente presente
 *  ou não. Há um campo para o Nível de Privilegio do Descritor (Descriptor
 *  Privilege Level (DPL)) para dizer que ring é o mais alto número que é
 *  permitido para usar a interrupção dada.
 *  	A maior diferença é o resto da definição da flag de acesso. Os 5-bits
 *  inferiores do byte de acesso é sempre atribuído para 01110 em binário.
 *  Isto é 14 em decimal. Aqui está uma tabela para dar a você uma representação
 *  gráfica melhor do byte de acesso para uma entrada IDT.
 *
 *  7 | 6  5 | 4	              0
 *  P	 DPL    Sempre 01110 (14)
 *
 *  P - Segmento está presente? (1 = Sim)
 *  DPL - Qual Ring (0 to 3)
 */

#include <system.h>

//Define uma entrada IDT (Interrupt Descriptor Table)
struct IDTEntry {
    ushort baseLow;   // The lower 16 bits of the address to jump to when this interrupt fires.
    ushort sel;        //Nosso segmento do kernel vai aqui!
    uchar always0;     //Este vai ser sempre ser atribuído para 0!
    uchar flags;       //Conjunto usando a tabela acima!
    ushort baseHigh;
} __attribute__((packed));

// A struct describing a pointer to an array of interrupt handlers.
// This is in a format suitable for giving to 'lidt'.
struct IDTptr {
    ushort limit;
    uint base;
} __attribute__((packed));

/**
 * 		Declara uma IDT de 256 entradas.
 * Embora nós vamos usar apenas as primeiras 32 entradas,
 * o resto existe como um bit de uma armadilhamas (Traps).
 * 		Se qualquer entrada IDT indefinida for acionada, ela normalmente causar�
 * uma "Unhandled Interrupt" exception (Interrupção Intratável).
 * Qualquer descritor para que o bit de 'presença' esteja limpo (0) vai gerar
 * uma exceção de "Interrupção Intratável"
 */
struct IDTEntry idt[256];
struct IDTptr idtp;

/**
 * Esta funcao está em loader.asm
 * Usada para carregar nosso IDT
*/
extern void idt_flush(uint);

/**
 * Funçao usada para setar uma entrada no IDT.
 * Muito mais simples que 'brincar' com a GDT
 */
void idtSetGate(unsigned char num, unsigned long base, unsigned short sel,
		unsigned char flags) {
	//O endereço base das rotinas de interrupção
    idt[num].baseLow = (base & 0xFFFF);
    idt[num].baseHigh = (base >> 16) & 0xFFFF;

    /**
     * O segmento, ou 'seletor' que está na  entrada o IDT vai usar
     * é setada aqui, junto com qualquer access flag
     */
    idt[num].sel = sel;
    idt[num].always0 = 0;
    idt[num].flags = flags;
}

/**
 * Intala o IDT
 */
void idtInstall() {
	//Atribui o ponteiro especial para o IDT, assim como em 'gdt.c'
    idtp.limit = (sizeof (struct IDTEntry) * 256) - 1;
    idtp.base = (uint)&idt;

    //Limpa o IDT inteiro, inicializando tudo em 0 (zero)
    memset(&idt, 0, sizeof(struct IDTEntry) * 256);

    //Adicione qualquer novo ISR para o IDT aqui, usando idtSetGate:
    {}

    //Aponta o registro interno do processador para a nova IDT
    idt_flush((uint)&idtp);
}
